package token

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"time"
)

var (
	key        string
	expire     int64
	block      cipher.Block
	ciphertext = []byte("abcdef1234567890")
	iv         = ciphertext[:aes.BlockSize]
	decrypter  cipher.Stream
)

type Export struct {
	Id         int64       `json:"id,omitempty"` //用户ID
	CreateTime int64       `json:"c,omitempty"`  //生成时间
	Level      int64       `json:"l,omitempty"`  //用户等级
	TokenType  int64       `json:"t,omitempty"`  //token类型，pc,client,h5
	Info       interface{} `json:"i,omitempty"`  //用户信息
	Verify     string      `json:"v,omitempty"`  //校验
}

func SetKey(k string) {
	key = k
	var err error
	block, err = aes.NewCipher([]byte(key))
	if err != nil {
		panic(err)
	}
}

func SetExpire(e int64) {
	expire = e
}

func Md5(s string) string {
	h := md5.New()
	h.Write([]byte(s))
	return hex.EncodeToString(h.Sum(nil))
}

func Create(id, level, tokenType int64, info interface{}) (token string) {
	ex := Export{
		Info:       info,
		Id:         id,
		Level:      level,
		TokenType:  tokenType,
		CreateTime: time.Now().Unix(),
	}
	ex.Verify = Md5(fmt.Sprintf("%d%d%d%d", ex.Id, ex.Level, ex.CreateTime, ex.TokenType))
	msg, _ := json.Marshal(&ex)

	encrypted := make([]byte, len(msg))
	encrypter := cipher.NewCFBEncrypter(block, iv)
	encrypter.XORKeyStream(encrypted, msg)

	token = hex.EncodeToString(encrypted)
	return
}

func Parse(token string) (ex *Export, err error) {

	str, err := hex.DecodeString(token)
	if err != nil {
		return
	}
	decrypted := make([]byte, len(str))
	decrypter := cipher.NewCFBDecrypter(block, iv)
	decrypter.XORKeyStream(decrypted, str)
	ex = new(Export)
	err = json.Unmarshal(decrypted, ex)
	if err != nil {
		return
	}
	if ex.Verify != Md5(fmt.Sprintf("%d%d%d%d", ex.Id, ex.Level, ex.CreateTime, ex.TokenType)) {
		err = errors.New("错误的token:" + token)
		return
	}
	return
}

// false 未过期
// true  过期
func (e *Export) IsExpire() bool {
	return !((time.Now().Unix() - e.CreateTime) < expire)
}
func (e *Export) GetInfo() interface{} {
	return e.Info
}
func (e *Export) GetId() int64 {
	return e.Id
}
func (e *Export) GetLevel() int64 {
	return e.Level
}

func (e *Export) GetTokenType() int64 {
	return e.TokenType
}
